# $Id$



from basic_types.factory import Factory
from basic_types.num_record import *
from basic_types.marshaller import XDRMarshaller
from basic_types.marshaller import XDRUnmarshaller
import binascii


__all__ = ["RecordsPackXDR"]


class RecordsPackXDR(object):
    """
    Set of records. Main purpose: define the marshalling and unmarshalling methods.
    """
    def __init__( self, records=None ):
        if records is not None and len( records ) > 0:
            self.records = records
        else:
            self.records = []

    def __repr__( self ):
        return "%s(%s)" % ( self.__class__.__name__, ", ".join( [repr(r) for r in self.records] ) )

    def __str__( self ):
        return self.__repr__()

    def get_records( self ):
        return self.records

    def pack( self ):
        """
        Pack all records into a buffer as binary string. First word in the buffer holds the number of records, the last one is a crc32.
        Format:
            - 4 bytes : length of whole thing in bytes (to allow to jump over it easily)
            - 4 bytes : number of records in buffer
            - N bytes : the marshalled records
            - 4 bytes : 32 bit CRC checksum
            - 4 bytes : length of whole thing in bytes (to allow to jump/seek backwards)
        """
        xdr_marshaller = XDRMarshaller()
        xdr_marshaller.writeInt32( "len", len( self.records ) )
        for record in self.records:
            record.marshal( xdr_marshaller )
        crc = binascii.crc32( xdr_marshaller.get_buffer() ) & 0xffffffff
        xdr_marshaller.writeUint32( "crc", crc )
        payload = xdr_marshaller.get_buffer()

        pack_bytes = XDRMarshaller()
        pack_bytes.writeInt32( "bytes", len( payload ) + 4 + 4 )
        packed_msg = pack_bytes.get_buffer() + payload + pack_bytes.get_buffer()
        return packed_msg

    def unpack( self, buff, record_type ):
        records = []
        if len( buff ) < 16:  # must have at least start, end markers, length and crc.
            raise EOFError, "Buffer too short!"
        pack_bytes_start = buff[0:4]
        pack_bytes_end = buff[-4:]
        if pack_bytes_start != pack_bytes_end:
            print "Buffer length marks differ: start:%d, end:%d" % (pack_bytes_start, pack_bytes_end)
        payload_buff = buff[4:-4]
        
        #print "payload_buff:", binascii.hexlify( payload_buff )
        crc_buffer = binascii.crc32( payload_buff[:-4] ) & 0xffffffff
        xdr_unmarshaller = XDRUnmarshaller( payload_buff )
        __len = xdr_unmarshaller.readInt32( "len" )
        for __i in xrange( __len ):
            record = Factory.build( record_type )
            record.unmarshal( xdr_unmarshaller )
            records.append( record )
        crc = xdr_unmarshaller.readUint32( "crc" )
        if crc_buffer != crc:
            print "CRC mismatch! buffer:%d vs. computed:%d" % (crc, crc_buffer)
        self.records = records
        return self


if __name__ == "__main__":
    from time import time

    recs = []
    for __i in xrange( 3 ):
        rec = NumRecord( long(time() * NS_IN_S) + __i, float(__i) )
        recs.append( rec )
    print recs   
    rp = RecordsPackXDR( recs )
    buff = rp.pack()
    print binascii.hexlify(buff)
    new_recs = RecordsPackXDR().unpack( buff, "NumRecord" ).get_records()
    print new_recs

    